local M = {
  -- 代码智能补全
  "hrsh7th/nvim-cmp",
  enabled = true,
  event = { "BufReadPre", "BufNewFile" },
  after = { "cmp-nvim-lsp" }, -- 确保数据源已就绪
  dependencies = {
    -- "saghen/blink.cmp", -- 最新的rust重写

    "hrsh7th/nvim-cmp", -- 补全引擎
    "hrsh7th/cmp-nvim-lsp", -- LSP 补全源

    "hrsh7th/cmp-buffer", -- 缓冲区补全
    "hrsh7th/cmp-path", -- 路径补全
    "hrsh7th/cmp-cmdline", -- 命令行补全

    "hrsh7th/cmp-nvim-lua", -- lua 的补全
    "hrsh7th/cmp-emoji", -- emoji 补全
    "hrsh7th/cmp-calc",
    "hrsh7th/cmp-nvim-lsp-document-symbol",
    "hrsh7th/cmp-nvim-lsp-signature-help",
    "FelipeLema/cmp-async-path", -- { name = 'async_path' }

    -- 使用LuaSnip作为补全源
    "saadparwaiz1/cmp_luasnip", -- LuaSnip 补全源
    {
      "L3MON4D3/LuaSnip", -- 代码片段引擎
      build = (function()
        if vim.fn.has("win32") == 1 or vim.fn.executable("make") == 9 then
          return
        end
        return "make install_jsregexp"
      end)(),
      dependencies = {
        -- 提供扩展补全体验
        "rafamadriz/friendly-snippets", -- 包含大部分常用语言的代码段
        "onsails/lspkind.nvim", -- 代码提示,显示分类的小图标支持
      },
    },
    "lukas-reineke/cmp-rg", -- { name = "rg" }
    "f3fora/cmp-spell",
    "ray-x/cmp-treesitter", -- { name = 'treesitter' },
    {
      "David-Kunz/cmp-npm", -- {name = "npm"}
      dependencies = { "nvim-lua/plenary.nvim" },
      ft = "json",
      config = function()
        require("cmp-npm").setup({})
      end,
    },

    "roobert/tailwindcss-colorizer-cmp.nvim",
    "mattn/emmet-vim",
  },
}

function M.config()
  vim.opt.completeopt = { "menu", "menuone", "noselect" }

  require("luasnip.loaders.from_vscode").lazy_load()
  require("luasnip.loaders.from_snipmate").lazy_load()

  local cmp = require("cmp")
  local luasnip = require("luasnip")
  local lspkind = require("lspkind")

  lspkind.init({
    mode = "symbol_text",
    preset = "codicons",
    symbol_map = {
      Text = "󰉿",
      Method = "󰆧",
      Function = "󰊕",
      Constructor = "",
      Field = "󰜢",
      Variable = "󰀫",
      Class = "󰠱",
      Interface = "",
      Module = "",
      Property = "󰜢",
      Unit = "󰑭",
      Value = "󰎠",
      Enum = "",
      Keyword = "󰌋",
      Snippet = "",
      Color = "󰏘",
      File = "󰈙",
      Reference = "󰈇",
      Folder = "󰉋",
      EnumMember = "",
      Constant = "󰏿",
      Struct = "󰙅",
      Event = "",
      Operator = "󰆕",
      TypeParameter = " ",

      --    Text = "",
      --    Method = "󰆧",
      --    Function = "󰊕",
      --    Constructor = "",
      --    Field = "󰇽",
      --    Variable = "󰂡",
      --    Class = "󰠱",
      --    Interface = "",
      --    Module = "",
      --    Property = "󰜢",
      --    Unit = "",
      --    Value = "󰎠",
      --    Enum = "",
      --    Keyword = "󰌋",
      --    Snippet = "",
      --    Color = "󰏘",
      --    File = "󰈙",
      --    Reference = "",
      --    Folder = "󰉋",
      --    EnumMember = "",
      --    Constant = "󰏿",
      --    Struct = "",
      --    Event = "",
      --    Operator = "󰆕",
      --    TypeParameter = "󰅲",
    },
  })

  local colorizer = require("tailwindcss-colorizer-cmp").formatter

  local select_opts = { behavior = cmp.SelectBehavior.Select }

  cmp.setup({
    snippet = {
      expand = function(args)
        luasnip.lsp_expand(args.body)
      end,
    },
    sources = {
      { name = "nvim_lsp" }, -- lsp cmp-nvim-lsp
      { name = "luasnip" }, -- luasnip snippets
      { name = "buffer", keyword_length = 2 },
      { name = "path", keyword_length = 2 }, -- file system paths
      { name = "async_path", keyword_length = 2 }, -- file system paths (async)
      { name = "rg", keyword_length = 3 }, -- rg search
      { name = "nvim_lsp_document_symbol", keyword_length = 3 }, -- document-symbol
      { name = "nvim_lsp_signature_help", keyword_length = 3 }, -- signature-help
      { name = "nvim_lua" },
      { name = "tailwindcss-colorizer-cmp" },
      {
        -- vim.opt.spell = true
        -- vim.opt.spelllang = { 'en_us', 'cjk' }
        name = "spell", -- cmp-spell
        option = {
          keep_all_entries = true,
          enable_in_context = function()
            return require("cmp.config.context").in_treesitter_capture("spell")
          end,
        },
        keyword_length = 3,
      },
      -- {
      --   name = "spell",   -- for markdown spellchecks completions
      --   option = {
      --     enable_in_context = function()
      --       local ft = vim.bo.filetype
      --       return ft == "markdown" or ft == "text"
      --     end,
      --   },
      -- },
      { name = "calc" },
      { name = "treesitter", keyword_length = 4 }, -- cmp-treesitter
      { name = "npm", keyword_length = 2 }, -- package.json  npm
      { name = "crates" }, -- rust -> crates
      { name = "emoji" },
      { name = "copilot" }, -- "zbirenbaum/copilot-cmp",

      { name = "emmet_vim" }, -- 如果需要额外的 Emmet 支持
    },
    window = {
      documentation = cmp.config.window.bordered(),
    },
    experimental = {
      -- 编辑器像有一个灰色倒影的预测用户输入书写提示
      ghost_text = true,
      native_menu = false,
    },
    formatting = {
      -- fields = { 'menu', 'abbr', 'kind' },
      fields = { "abbr", "kind", "menu" },
      -- Source 显示提示来源 lsp, snippets, path 等等
      format = function(entry, item)
        -- local menu_icon = {
        -- 	nvim_lsp = 'λ',
        -- 	luasnip = '⋗',
        -- 	buffer = 'Ω',
        -- 	path = '🖫',
        -- }
        -- item.menu = menu_icon[entry.source.name]
        -- return item

        item.menu = "[" .. string.upper(entry.source.name) .. "]"

        if vim.tbl_contains({ "path" }, entry.source.name) then
          local icon, hl_group = require("nvim-web-devicons").get_icon(
            entry:get_completion_item().label
          )
          if icon then
            item.kind = icon
            item.kind_hl_group = hl_group
            return item
          end
        end

        if entry.source.name == "crates" then
          vim.api.nvim_set_hl(0, "CmpItemKindCrate", { fg = "#F64D00" })
          item.kind = " "
          item.kind_hl_group = "CmpItemKindCrate"
        end

        if entry.source.name == "emoji" then
          vim.api.nvim_set_hl(0, "CmpItemKindEmoji", { fg = "#FDE030" })
          item.kind = ""
          item.kind_hl_group = "CmpItemKindEmoji"
        end

        -- use lspkind and tailwindcss-colorizer-cmp for additional formatting
        item = lspkind.cmp_format({
          maxwidth = 25,
          ellipsis_char = "...",
        })(entry, item)

        if entry.source.name == "nvim_lsp" then
          item = colorizer(entry, item)
        end

        return require("lspkind").cmp_format({ with_text = true })(entry, item)
      end,
    },
    mapping = {
      ["<C-k>"] = cmp.mapping.select_prev_item(), -- previous suggestion
      ["<C-j>"] = cmp.mapping.select_next_item(), -- next suggestion
      ["<Up>"] = cmp.mapping.select_prev_item(select_opts),
      ["<Down>"] = cmp.mapping.select_next_item(select_opts),
      ["<C-p>"] = cmp.mapping.select_prev_item(select_opts),
      ["<C-n>"] = cmp.mapping.select_next_item(select_opts),

      -- ["<C-b>"] = cmp.mapping.scroll_docs(-4),
      -- ["<C-f>"] = cmp.mapping.scroll_docs(4),
      ["<C-u>"] = cmp.mapping.scroll_docs(-4),
      ["<C-d>"] = cmp.mapping.scroll_docs(4),

      ["<C-Space>"] = cmp.mapping.complete(), -- show completion suggestions -- 手动触发补全
      ["<C-e>"] = cmp.mapping.abort(), -- close completion window
      ["<C-y>"] = cmp.mapping.confirm({ select = false }),
      ["<CR>"] = cmp.mapping.confirm({ select = true }), -- 确认选择

      ["<C-f>"] = cmp.mapping(function(fallback)
        if luasnip.jumpable(1) then
          luasnip.jump(1)
        else
          fallback()
        end
      end, { "i", "s" }),
      ["<C-b>"] = cmp.mapping(function(fallback)
        if luasnip.jumpable(-1) then
          luasnip.jump(-1)
        else
          fallback()
        end
      end, { "i", "s" }),

      ["<Tab>"] = cmp.mapping(function(fallback)
        local col = vim.fn.col(".") - 1

        if cmp.visible() then
          cmp.select_next_item(select_opts)
        elseif col == 0 or vim.fn.getline("."):sub(col, col):match("%s") then
          fallback()
        else
          cmp.complete()
        end
      end, { "i", "s" }),
      ["<S-Tab>"] = cmp.mapping(function(fallback)
        if cmp.visible() then
          cmp.select_prev_item(select_opts)
        else
          fallback()
        end
      end, { "i", "s" }),
    },
  })

  -- Emmet 特定的补全配置
  cmp.setup.filetype({ "html", "javascriptreact", "typescriptreact", "vue" }, {
    sources = cmp.config.sources({
      { name = "emmet_vim", priority = 100 }, -- 高优先级
      { name = "nvim_lsp", priority = 90 },
      { name = "luasnip", priority = 80 },
    }, {
      { name = "buffer" },
    }),
  })

  -- 根据文件类型来选择补全来源
  cmp.setup.filetype("gitcommit", {
    sources = cmp.config.sources({
      { name = "git" },
      { name = "buffer" },
    }),
  })
  cmp.setup.filetype("markdown", {
    sources = cmp.config.sources({
      { name = "path" },
      { name = "buffer" },
    }),
  })
  -- 命令模式下输入 `/` 、`?`启用补全
  cmp.setup.cmdline({ "/", "?" }, {
    mapping = cmp.mapping.preset.cmdline({
      -- 覆盖默认的快捷键
      ["<CR>"] = {
        c = cmp.mapping.confirm({ select = false }),
      },
    }),
    sources = {
      { name = "buffer" },
    },
  })
  -- 命令模式下输入 `:` 启用补全
  cmp.setup.cmdline(":", {
    mapping = cmp.mapping.preset.cmdline({
      -- 覆盖默认的快捷键
      ["<CR>"] = {
        c = cmp.mapping.confirm({ select = false }),
      },
    }),
    sources = cmp.config.sources({
      { name = "path" },
      { name = "cmdline" },
    }),
  })
end

return M
